<!-- 调用链明细 -->
<template>
    <div class="all">
        <div>
            <let-form size="small" inline>
                <let-form-item :label="$t('callChain.date')">
                    <let-date-picker v-model="today" style="width: 200px"></let-date-picker>
                </let-form-item>
                <let-form-item :label="$t('callChain.btime')">
                    <let-time-picker v-model="dateTime[0]" style="width: 200px"></let-time-picker>
                </let-form-item>
                <let-form-item :label="$t('callChain.etime')">
                    <let-time-picker v-model="dateTime[1]" style="width: 200px"></let-time-picker>
                </let-form-item>
                <let-form-item>
                    <let-button theme="primary" style="margin-left: 10px; margin-bottom: 2px" @click="sub">
                        {{ $t("callChain.sub") }}
                    </let-button>
                </let-form-item>
            </let-form>
        </div>
        <div style="height: 500px;">
            <div class="block">
                <let-pagination
                    :total="total % fsize ? parseInt(total / fsize) + 1 : total / fsize"
                    :page="currentPage"
                    size="small"
                    no-border
                    show-sums
                    :sum="total"
                    jump
                    @change="handleCurrentChange">
                </let-pagination>
            </div>
            <vue-scroll :ops="ops" style="width: 100%; height: 100%;margin-top: 5px">
                <let-table :data="dataarrs" :stripe="true" :empty-msg="$t('callChain.noResult')"
                           border="true" style="width: 100%; height: 580px" :sortable="true" ref="detailsTable">
                    <let-table-column prop="name" title="ID"></let-table-column>
                    <let-table-column prop="timeStamp" :title="$t('callChain.btime')" width="15%">
                        <template slot-scope="scope">{{ scope.row.startTime }}</template>
                    </let-table-column>
                    <let-table-column prop="timeFrame" :title="$t('callChain.etime')" width="15%">
                        <template slot-scope="scope">{{ scope.row.endTime }}</template>
                    </let-table-column>
                    <let-table-column prop="timeFrame" :title="$t('callChain.allTime')" width="10%">
                        <template slot-scope="scope"> {{ scope.row.callTime }}ms</template>
                    </let-table-column>
                    <let-table-column align="center" :title="$t('callChain.operation')" width="10%">
                        <template slot-scope="scope">
                            <let-table-operation @click="handleEdit(scope.row)">{{$t("callChain.detail")}}</let-table-operation>
                        </template>
                    </let-table-column>
                </let-table>
            </vue-scroll>
            <let-modal v-model="dialogVisible1s" width="90%" style="height: 1200px;top:-120px" :footShow="false">
                <div style="height: 800px">
                    <vue-scroll :ops="ops" style="width: 100%; height: 100%">
                        <gantt-chart
                            v-bind:nodeinfos="nodeinfos"
                            v-bind:serverArrs="serverArrs"
                            v-bind:edges="edges"
                            v-bind:traceIds="traceIds"
                            v-bind:funcIds="funcIds"
                            v-bind:values="value"
                            v-bind:dialogVisible1s="dialogVisible1s">
                        </gantt-chart>
                    </vue-scroll>
                </div>
            </let-modal>
        </div>
        <opendiv
            v-bind:flag="flag"
            v-bind:res="res"
            v-bind:rest="rest"
            v-bind:title="title">
        </opendiv>
    </div>
</template>

<script>
import dagreD3 from "dagre-d3";
import * as d3 from "d3";
import ganttChart from "./gantt-chart";
import opendiv from "./opendiv";
import moment from 'moment';
import {getAll, unique, transTree, transTree1} from "../../api/util";

export default {
    components: {ganttChart, opendiv: opendiv,},
    data() {
        return {
            dialogVisible1s: false,
            form: {},
            flag: false,
            res: "",
            rest: "",
            title: "",
            flags: false,
            vname: "",
            options: [],
            resdata: [],
            restdata: [],
            value: "",
            serverArrs: [],
            datalist: [],
            dataLists: [],
            dataarr: [],
            traceIds: [],
            nodeinfos: [],
            edges: [],
            funcIds: [],
            begin: "",
            value1: "",
            today: "",
            dateTime: ["", ""],
            fsize: 10,
            currentPage: 1,
            total: 0,
            dataarrs: [],
            ops: {
                vuescroll: {},
                scrollPanel: {},
                rail: {
                    keepShow: true,
                },
                bar: {
                    hoverStyle: true,
                    onlyShowBarOnScroll: false, //是否只有滚动的时候才显示滚动条
                    background: "#F5F5F5", //滚动条颜色
                    opacity: 0.5, //滚动条透明度
                    "overflow-x": "hidden",
                },
            },
        };
    },
    mounted() {
        let startTime = new Date().setHours(0, 0, 0, 0);
        let endTime = new Date().setHours(23, 59, 59, 999);
        this.value1 = [startTime, endTime];
        var myDate = new Date();
        let month = myDate.getMonth() + 1;
        if (month >= 1 && month <= 9) {
            month = "0" + month;
        }
        let day = myDate.getDate();
        if (day >= 1 && day <= 9) {
            day = "0" + day;
        }
        this.today = myDate.getFullYear() + "-" + month + "-" + day;
        this.dateTime = ["00:00", "23:59"];
        this.vname =
            this.$store.state.name.name.application +
            "." +
            this.$store.state.name.name.server_name;
        this.sub();
    },
    methods: {
        handleCurrentChange(val) {
            this.currentPage = val;
            let beg = (this.currentPage - 1) * this.fsize;
            let end = beg + this.fsize;
            this.dataarrs = this.dataarr.slice(beg, end);
        },
        fortime(value) {
            let date = new Date(value);
            value = value + "";
            let str = value.substring(value.length - 3);
            return `${date.getFullYear()}-${
                date.getMonth() + 1
            }-${date.getDate()} ${date.getHours()}:${date.getMinutes()}:${date.getSeconds()}.${str}`;
        },
        async sub() {
            this.form.label = this.vname;
            if (!this.dateTime[0]) {
                this.$Notice({
                    message: this.$t("callChain.sbtime"),
                    type: "warning",
                });

                return;
            }
            if (!this.dateTime[1]) {
                this.$Notice({
                    message: this.$t("callChain.setime"),
                    type: "warning",
                });

                return;
            }
            this.form.stime = `${this.today} ${this.dateTime[0]}`;
            this.form.etime = `${this.today} ${this.dateTime[1]}`;
            this.form.stime = new Date(this.form.stime).getTime();
            this.form.etime = new Date(this.form.etime).getTime();
            this.form.nowDate = this.today;

            if (this.form.nowDate == "") {
                this.$Notice({
                    message: this.$t("callChain.sdata"),
                    type: "error",
                });

                return;
            }
            let rest = []
            try {
                this.$refs.detailsTable.$loading.show();
                rest = await this.$ajax.getJSON("/server/api/detailByStartEndTime", this.form);
                this.$refs.detailsTable.$loading.hide();
            } catch (e) {
                this.$refs.detailsTable.$loading.hide();
            }
            this.options = [];
            this.flags = true;
            this.dataarr = [];
            for (const tmp of rest) {
                if (tmp.endTime == 0 || tmp.startTime == 0) {
                    continue;
                }
                let newValue = {};
                newValue.name = tmp.name;
                newValue.callTime = tmp.endTime - tmp.startTime;
                newValue.startTime = moment(tmp.startTime).format("YYYY-MM-DD HH:mm:ss");
                newValue.endTime = moment(tmp.endTime).format("YYYY-MM-DD HH:mm:ss");
                this.dataarr.push(newValue)
            }
            this.total = this.dataarr.length;
            this.handleCurrentChange(1);
        },
        async handleEdit(row) {
            let reg = new RegExp("-", "g")
            let date = this.form.nowDate.replace(reg, "");
            let res = await this.$ajax.getJSON("/server/api/detailByTraceId", {
                id: row.name,
                nowDate: this.form.nowDate.replace(reg, ""),
            });

            this.title = "TraceId：" + row.name;
            this.res = res;
            this.flag = true;
            return;

            this.resdata = [];
            this.restdata = [];
            let nodeinfos = [];
            let edges = [];
            this.traceIds = [];
            this.funcIds = [];
            let i = -1;
            this.serverArrs = [];
            this.dialogVisible1s = true;
            this.dataarr.forEach((item) => {
                if (item.id == row.id) {
                    this.serverArrs.push(item.id);
                    this.traceIds.push(item.traceIds);
                    this.funcIds.push(item.funcIds);
                    i++;
                    nodeinfos[i] = [];
                    edges[i] = [];
                    this.resdata[this.resdata.length] = item.vertexes;
                    this.restdata[this.restdata.length] = item.edges;

                    let item1 = item.vertexes;
                    let item2 = item.edges;

                    item1.forEach((item) => {
                        nodeinfos[i].push({
                            id: item.vertex,
                            label: item.vertex,
                            time: (item.callTime / item.callCount).toFixed(2),
                        });
                    });

                    item2.forEach((item) => {
                        edges[i].push({
                            startTimeStamp: item.startTimeStamp,
                            source: item.fromVertex,
                            target: item.toVertex,
                            label: (item.callTime / item.callCount).toFixed(2),
                        });
                    });
                }
            });

            edges.forEach((item, index) => {
                let arr = [];
                item.forEach((items) => {
                    arr.push(items.target);
                });
                item.forEach((items) => {
                    if (arr.indexOf(items.source) < 0) {
                        this.begin = items.source;
                        nodeinfos[index].push({
                            id: items.source,
                            label: items.source,
                        });
                    } else {
                    }
                });
            });
            this.edges = edges;
            this.nodeinfos = nodeinfos;
            var svg = document.getElementById("svgs");
            var containers = document.getElementsByClassName("containers");
            for (var k in containers) {
                if (containers[k].style) {
                    containers[k].style.display = "none";
                }
            }
            this.becomeD3(edges, nodeinfos);
        },
        becomeD3(edges, nodeinfos) {
            this.$nextTick(() => {
                for (let k in edges) {
                    let g = new dagreD3.graphlib.Graph().setGraph({});
                    g.setGraph({
                        rankdir: "LR",
                        marginy: 60,
                    });
                    nodeinfos[k].forEach((item, index) => {
                        item.rx = item.ry = 5; //圆角
                        g.setNode(item.id, {
                            labelType: "html",
                            label: item.time
                                ? `<div class="wrap"> <span class="span1">${item.id}</span><p class="p1" style="height:20px" >( ${item.time} ms )</p></div>`
                                : `<div class="wrap"><span class="span1">${item.id}</span ></div>`,
                            style: "fill:#457ff5",
                        });
                    });
                    // 链接关系
                    edges[k].forEach((item) => {
                        if (k % 2 == 0) {
                            g.setEdge(item.source, item.target, {
                                label: item.label + "ms",
                                //边样式
                                style: "fill:#fff;stroke:#333;stroke-width:1.5px;",
                            });
                        } else {
                            g.setEdge(item.source, item.target, {
                                label: item.label + "ms",
                                //边样式
                                style: "fill:#EBF2FF;stroke:#333;stroke-width:1.5px;",
                            });
                        }
                    });

                    //绘制图形
                    let svg = d3.select(`.svgs${k} svg`);
                    let inner = svg.select("g");
                    let render = new dagreD3.render();
                    render(inner, g);

                    var classSvg = document.getElementsByClassName(`svg${k}`);

                    inner.selectAll(".edgeLabel tspan").attr("dy", "-1.2em");
                    let spans = document.querySelectorAll(`.svgs${k} svg .span1`);
                    for (var j in spans) {
                        if (j == spans.length) {
                            break;
                        }
                        // spans[j].style.fontSize = "12px";
                    }
                    inner.selectAll("g.node").on("click", (e, d, c) => {
                        let arr = [];
                        let svgs = d3.select(`#svgs`);
                        svgs.selectAll(".all rect").style("fill", "white");
                        edges[k].forEach((items, j) => {
                            if (e == items.source && this.begin != items.target) {
                                arr.push(items);
                            }
                        });

                        inner.selectAll("rect").style("fill", (val, cd) => {
                            if (val == e && arr.length > 0) {
                                return "#3F5AE0";
                            } else {
                                return "#457FF5";
                            }
                        });

                        if (arr.length > 0) {
                            getAll(arr, edges[k], 0, arr.length, e);
                        } else {
                            return;
                        }

                        let arrs = [];
                        let length = -1;
                        for (let k in arr) {
                            if (arr[k - 1] && arr[k].source == arr[k - 1].source) {
                                arrs[length].push(arr[k]);
                            } else {
                                length += 1;
                                arrs[length] = [];
                                arrs[length].push(arr[k]);
                            }
                        }
                        arrs.forEach((item) => {
                            item.sort((a, b) => {
                                return a.startTimeStamp - b.startTimeStamp;
                            });
                        });
                        let array = [];
                        arrs.forEach((item) => {
                            item.forEach((items) => {
                                array.push(items);
                            });
                        });
                        this.ganTe(array, e, k);
                    });
                }
            });
        },

        async ganTe(a, d, index) {
            var containers = document.getElementsByClassName("containers");

            for (var k in containers) {
                if (containers[k].style) {
                    containers[k].style.display = "none";
                }
            }
            var container = document.getElementById(`containers${index}`);
            container.style.display = "block";
            let obj = a;

            let beginArr = [];
            obj.forEach((item) => {
                beginArr.push(item.startTimeStamp);
                this.resdata[index].forEach((items) => {
                    if (item.target == items.vertex) {
                        item.server_time = items.callTime / items.callCount;
                    }
                });
                this.restdata[index].forEach((items) => {
                    if (
                        item.source == items.fromVertex &&
                        item.target == items.toVertex
                    ) {
                        item.client_time = items.callTime / items.callCount;
                    }
                });
            });
            let sourceArr = [];
            let targetArr = [];
            let servers = [];
            let clientTime = [];
            let serverTime = [];
            let btimeArr = [];
            transTree1(
                obj,
                sourceArr,
                targetArr,
                servers,
                clientTime,
                serverTime,
                btimeArr
            );
            var nowDate = new Date(); //今日日期为2020年2月14日
            var nowDateStr = formatDate(nowDate);

            function formatDate(date) {
                var arr = date.toLocaleDateString().split("/");
                if (arr[1].length < 2) {
                    arr.splice(1, 1, "0" + arr[1]);
                }
                if (arr[2].length < 2) {
                    arr.splice(2, 1, "0" + arr[2]);
                }
                return arr.join("-");
            }

            let title = d;
            var echarts = require("echarts");
            var myChart1 = echarts.init(container);
            let data = [];
            let length = servers.length - 1;
            for (let k in servers) {
                data.push({
                    name: servers[k],
                    value: [
                        length - parseInt(k),
                        btimeArr[k],
                        btimeArr[k] + clientTime[k],
                        (clientTime[k] - serverTime[k]) / 2 + btimeArr[k],
                        serverTime[k],
                    ],
                });
            }

            let numArr = [];
            let numArr1 = [];
            data.forEach((item) => {
                numArr.push(item.value[2]);
                numArr1.push(item.value[1]);
            });
            numArr.sort((a, b) => {
                return b - a;
            });
            numArr1.sort((a, b) => {
                return a - b;
            });
            servers = servers.reverse();
            var categories = servers;

            function renderItem(params, api) {
                var categoryIndex = api.value(0); //取出data中数据项的第一个维度的值
                //===============计划工期进度条
                //计划开始日期(在屏幕上的像素值)
                var planStartDate = api.coord([api.value(1), categoryIndex]); //将数据项中的数值对应的坐标系上的点，转换为屏幕上的像素值
                //坐标系上的点：是数据项映射到坐标系的x轴和y轴后，对应的位置
                //屏幕上的像素值：是坐标系上的点，在屏幕上的位置
                //计划结束日期(在屏幕上的像素值)
                var planEndDate = api.coord([api.value(2), categoryIndex]);
                //由于data.value中维度1和维度2的数据会被映射到x轴，而x轴的type为time，即时间轴，
                //所以api.value(1)和api.value(2)获取到的值是将日期转换后的毫秒值
                //设置图形的高度
                var height =
                    api.size([0, 1])[1] * 0.35 > 30 ? 30 : api.size([0, 1])[1] * 0.35; //获得Y轴上数值范围为1的一段所对应的像素长度；这是官方文档的注释，对于api.size()方法，目前我还不是很理解；先做个标记??? 以后再说
                //console.log("height", height);
                //使用graphic图形元素组件，绘制矩形
                //clipRectByRect方法，在绘制矩形时，如果矩形大小超出了当前坐标系的包围盒，则裁剪这个矩形
                var rectShape1 = echarts.graphic.clipRectByRect(
                    {
                        //矩形的位置
                        x: planStartDate[0],
                        y: planStartDate[1],
                        //矩形的宽高
                        width: planEndDate[0] - planStartDate[0],
                        height: height,
                    },
                    {
                        //当前坐标系的包围盒
                        x: params.coordSys.x,
                        y: params.coordSys.y,
                        width: params.coordSys.width,
                        height: params.coordSys.height,
                    }
                );
                //===============实际工期进度条
                var rectShape2 = null;
                //判断实际开始日期是否为空，如果为空，说明项目还没开始
                if (api.value(3) !== "") {
                    //如果实际开始日期不为空
                    //实际开始日期(在屏幕上的像素值)
                    var practiceStartDate = api.coord([api.value(3), categoryIndex]);
                    //当前日期或实际结束日期
                    var nowDate_or_practiceEndDate;
                    //项目周期(毫秒值)：计划结束日期(毫秒值) - 计划开始日期(毫秒值)
                    var projectCycle_millisecond = api.value(4);
                    //实际开始日期(毫秒值)
                    var practiceStartDate_millisecond = api.value(3);

                    //当前日期(毫秒值)
                    var nowDate_millisecond = +echarts.number.parseDate(nowDateStr);

                    //如果项目进度未完成或刚好完成。
                    //比如计划开始时间1月10日，计划结束时间1月20日，项目周期10天，实际开始时间1月10日，当前日期1月18日，说明项目进度未完成
                    //那么在渲染实际工期的进度条时，进度条的范围为1月10日至1月18日(实际开始日期至当前日期)
                    //比如计划开始时间1月10日，计划结束时间1月20日，项目周期10天，实际开始时间1月10日，当前日期1月20日，说明项目进度刚好完成
                    //那么在渲染实际工期的进度条时，进度条的范围为1月10日至1月20日(实际开始日期至当前日期)
                    //比如计划开始时间1月10日，计划结束时间1月20日，项目周期10天，实际开始时间1月10日，当前日期1月22日，说明项目已结束
                    //那么在渲染实际工期的进度条时，进度条的范围应该是1月10日至1月20日(实际开始日期至实际结束日期)，
                    //而不是1月10日至1月22日(实际开始日期至当前日期)
                    //实际结束日期(毫秒值)：实际开始日期(毫秒值) + 项目周期(毫秒值)
                    var practiceEndDate_millisecond =
                        practiceStartDate_millisecond + projectCycle_millisecond;
                    //取实际结束日期(在屏幕上的像素值)
                    nowDate_or_practiceEndDate = api.coord([
                        practiceEndDate_millisecond,
                        categoryIndex,
                    ]);

                    //使用graphic图形元素组件，绘制矩形
                    //clipRectByRect方法，在绘制矩形时，如果矩形大小超出了当前坐标系的包围盒，则裁剪这个矩形
                    rectShape2 = echarts.graphic.clipRectByRect(
                        {
                            //矩形的位置
                            x: practiceStartDate[0],
                            y: practiceStartDate[1],
                            //矩形的宽高
                            width: nowDate_or_practiceEndDate[0] - practiceStartDate[0],
                            height: height,
                        },
                        {
                            //当前坐标系的包围盒
                            x: params.coordSys.x,
                            y: params.coordSys.y,
                            width: params.coordSys.width,
                            height: params.coordSys.height,
                        }
                    );
                }

                if (rectShape2 == null) {
                    //设置绘制的矩形的元素定义
                    return (
                        rectShape1 && {
                            type: "group",
                            children: [
                                {
                                    //类型为矩形
                                    type: "rect",
                                    //具体形状
                                    shape: rectShape1,
                                    //样式
                                    style: api.style({
                                        fill: "#AACCF9",
                                    }),
                                },
                            ],
                        }
                    );
                } else {
                    //渲染计划工期和实际工期
                    //设置绘制的矩形的元素定义
                    return (
                        rectShape1 &&
                        rectShape2 && {
                            type: "group",
                            children: [
                                {
                                    //类型为矩形
                                    type: "rect",
                                    //具体形状
                                    shape: rectShape1,
                                    //样式
                                    style: api.style({
                                        fill: "#a1a8f5",
                                    }),
                                },
                                {
                                    //类型为矩形
                                    type: "rect",
                                    //具体形状
                                    shape: rectShape2,
                                    //样式
                                    style: api.style({
                                        fill: "#9c9c9c",
                                    }),
                                },
                            ],
                        }
                    );
                }
            }

            var that = this;
            myChart1.setOption({
                tooltip: {
                    formatter: function (params) {
                        //params为当前点击图形元素的数据信息的对象
                        //计划开始时间

                        return (
                            params.name +
                            "<br/>" +
                            that.$t("callChain.stringBtime") +
                            that.fortime(params.value[1]) +
                            "ms" +
                            "<br/>" +
                            that.$t("callChain.stringEtime") +
                            that.fortime(params.value[2]) +
                            "ms" +
                            "<br/>" +
                            that.$t("callChain.clientTime") +
                            (params.value[2].toFixed(2) - params.value[1].toFixed(2)) +
                            "ms" +
                            "<br/>" +
                            that.$t("callChain.serverTime") +
                            params.value[4].toFixed(2) +
                            "ms"
                        );
                    },
                },
                title: {
                    text: this.serverArrs[index] + "：" + title,
                    left: "center",
                },
                xAxis: {
                    type: "value", //x轴类型为时间轴
                    min: numArr1[0], //最小值为2020-01-01
                    max: numArr[0], //最大值为2020-12-31
                    axisLabel: {
                        interval: 0,
                        formatter: function (value) {
                            return that.fortime(value);

                            //  let date= new Date(value)
                            //   value=value+""
                            //  let str = value.substring(value.length-3)

                            //  return `${date.getHours()}:${date.getMinutes()}:${date.getSeconds()}.${str}`;
                        },
                    },
                    splitLine: {
                        show: true, //  false没有网格线,true,有网格线
                    },
                },
                yAxis: {
                    data: categories,
                    axisTick: {
                        alignWithLabel: true, //保证刻度线和标签对齐，当boundaryGap为true的时候有效，不过boundaryGap默认就是true
                    },
                    splitLine: {
                        show: true, //  false没有网格线,true,有网格线
                    },
                },
                legend: {
                    left: "70%",
                    top: 20,
                    data: ["client", "server"],
                },
                grid: {
                    left: 260, // 调整这个属性
                },
                series: [
                    {
                        type: "custom",
                        //使用自定义的图形元素
                        renderItem: renderItem,
                        name: "client",
                        itemStyle: {
                            color: "#a1a8f5",
                        },
                        encode: {
                            //将维度1和维度2的数据映射到x轴
                            x: [1, 2],
                            //将维度0的数据映射到y轴
                            y: 0,
                        },
                        data: data,
                    },
                    //这个系列并没有太大作用，也没有给它设置data，只是为了通过这个系列，显示图例(legend)而已
                    {
                        type: "custom",
                        name: "server",
                        itemStyle: {
                            color: "#9c9c9c",
                        },
                    },
                ],
            });
        },
        async getdatas() {
        },
    },
};
</script>

<style>
.all svg {
    font-size: 12px;
}

.all .edgePath path {
    stroke: #606266;
    fill: #333;
    stroke-width: 1.5px;
}

.all #containers {
    width: 2000px;
    height: 500px;
    margin: 0 auto;
}

.all .el-button--primary {
    background-color: #3f5ae0;
    border-color: #3f5ae0;
    color: #fff;
}

.all .edgePath path {
    stroke: #999 !important;
}

.__bar-is-vertical {
    right: -1px !important;
}

.__bar-is-horizontal {
    display: none !important;
}

.node {
    background-color: #3f5ae0;
}
</style>




